package server;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat的主类
 */
public class Bootstrap {

  /**
   * 定义socket监听的端口号
   */
  private int port = 8080;

  public int getPort() {
    return port;
  }

  public void setPort(int port) {
    this.port = port;
  }


  /**
   * Minicat启动需要初始化展开的一些操作
   */
  public void start() throws Exception {
    loadProjects();

    // 定义一个线程池
    int corePoolSize = 10;
    int maximumPoolSize = 50;
    long keepAliveTime = 100L;
    TimeUnit unit = TimeUnit.SECONDS;
    BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
    ThreadFactory threadFactory = Executors.defaultThreadFactory();
    RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();


    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
      corePoolSize,
      maximumPoolSize,
      keepAliveTime,
      unit,
      workQueue,
      threadFactory,
      handler
    );





        /*
            完成Minicat 1.0版本
            需求：浏览器请求http://localhost:8080,返回一个固定的字符串到页面"Hello Minicat!"
         */
    ServerSocket serverSocket = new ServerSocket(port);
    System.out.println("=====>>>Minicat start on port：" + port);

        /*while(true) {
            Socket socket = serverSocket.accept();
            // 有了socket，接收到请求，获取输出流
            OutputStream outputStream = socket.getOutputStream();
            String data = "Hello Minicat!";
            String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length) + data;
            outputStream.write(responseText.getBytes());
            socket.close();
        }*/


    /**
     * 完成Minicat 2.0版本
     * 需求：封装Request和Response对象，返回html静态资源文件
     */
        /*while(true) {
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();

            // 封装Request对象和Response对象
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            response.outputHtml(request.getUrl());
            socket.close();

        }*/


    /**
     * 完成Minicat 3.0版本
     * 需求：可以请求动态资源（Servlet）
     */
        /*while(true) {
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();

            // 封装Request对象和Response对象
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            // 静态资源处理
            if(servletMap.get(request.getUrl()) == null) {
                response.outputHtml(request.getUrl());
            }else{
                // 动态资源servlet请求
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request,response);
            }

            socket.close();

        }
*/

        /*
            多线程改造（不使用线程池）
         */
        /*while(true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
            requestProcessor.start();
        }*/


    System.out.println("=========>>>>>>使用线程池进行多线程改造");
        /*
            多线程改造（使用线程池）
         */
    while (true) {
      Socket socket = serverSocket.accept();
      InputStream inputStream = socket.getInputStream();
      Request request = new Request(inputStream);
      Response response = new Response(socket.getOutputStream());

      int idx = request.getUrl().indexOf("/", 1);
      String project = idx != -1 ? request.getUrl().substring(1, idx) : "";
      String fixedUrl = idx != -1 ? request.getUrl().substring(idx) : request.getUrl();

      Map<String, HttpServlet> servletMap = projectServletMap.computeIfAbsent(project, (k) -> new HashMap<>());

      RequestProcessor requestProcessor = new RequestProcessor(socket, request, response, fixedUrl, servletMap);
      threadPoolExecutor.execute(requestProcessor);
    }
  }

  private final Map<String, Map<String, HttpServlet>> projectServletMap = new HashMap<>();

  private void loadProjects() {
    InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
    SAXReader saxReader = new SAXReader();

    try {
      Document document = saxReader.read(resourceAsStream);
      Element rootElement = document.getRootElement();

      @SuppressWarnings("unchecked")
      List<Element> webappElements = (List<Element>) rootElement.selectNodes("//webapp");

      for (Element element : webappElements) {
        String project = element.selectSingleNode("project").getStringValue();
        String root = element.selectSingleNode("root").getStringValue();

        Map<String, HttpServlet> servletMap = loadServlet(project, root);

        projectServletMap.put(project, servletMap);
      }


    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * 加载解析web.xml，初始化Servlet
   */
  private Map<String, HttpServlet> loadServlet(String project, String basePath) {
    String configPath = basePath + "/" + "web.xml";

    Map<String, HttpServlet> servletMap = new HashMap<>();
    InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(configPath);
    SAXReader saxReader = new SAXReader();

    try {
      Document document = saxReader.read(resourceAsStream);
      Element rootElement = document.getRootElement();

      @SuppressWarnings("unchecked")
      List<Element> selectNodes = rootElement.selectNodes("//servlet");

      for (Element element : selectNodes) {
        // <servlet-name>lagou</servlet-name>
        Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
        String servletName = servletNameElement.getStringValue();

        // <servlet-class>server.LagouServlet</servlet-class>
        Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
        String servletClass = servletClassElement.getStringValue();


        // 根据servlet-name的值找到url-pattern
        Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
        // /lagou
        String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

        // 为了方便测试，没有使用绝对路径，将编译后的class路径「/target/classes/」强制附加上了
        String absolutePath = new File("").getAbsolutePath();
        URL url = new URL("file://" + absolutePath + "/target/classes/" + basePath + "/");
        System.out.println(url);

        //
        URLClassLoader cl = new URLClassLoader(new URL[]{url});
        Class<?> servletType = cl.loadClass(servletClass);
        HttpServlet httpServlet = (HttpServlet) servletType.getConstructor().newInstance();

        servletMap.put(urlPattern, httpServlet);
      }


    } catch (Exception e) {
      e.printStackTrace();
    }

    return servletMap;
  }


  /**
   * Minicat 的程序启动入口
   *
   * @param args
   */
  public static void main(String[] args) {
    Bootstrap bootstrap = new Bootstrap();
    try {
      // 启动Minicat
      bootstrap.start();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
